iT邦幫忙

2024 iThome 鐵人賽

DAY 6
1
自我挑戰組

學習網頁開發系列 第 6

Django Model 繼承的方法

  • 分享至 

  • xImage
  •  

在 Django 中,Model 的繼承方式跟一般 python 的繼承方式大同小異。但是最基礎的 Class 一定要是 django.db.models.Model,這樣 Django 預設的 Model 行為才能正常運行。
主要要考慮的點只有一個:親層 Model 是否要為資料庫中的一個表,還是它只一個公用的模板,攜帶一些常見的屬性。

Django 官方推薦三種繼承方式:

  1. 當你希望用親層定義特定屬性,這樣眾多子層就不用重複書寫同樣內容時,我們可以使用 Abstract base classes
  2. 假如你希望親層自己為一個表,子層也為一個表,但子層會用到親層的屬性時。就可以使用 multi-table inheritance。這是最像一般 python 繼承的方法。
  3. 假如你想更改或改變某個 model 的方法,但不想或無法直接寫在該 model裡時,Proxy models是一個合適的方案。

Abstract Base Classes

寫法和一般 python 繼承大同小異,只不過需要增加一行設定。主要原因是因為,在 Django 中,只要繼承了 django.db.models.Model 的類別都會被拿來生成相應的表。但如上述,使用 Abstract Base Classes 就是想避免此情況。

from django.db import models
class AbstractModel(models.Model):
    name = models.CharField(max_length=50)
    
    def hey(self):
        print("hey")
        
    class Meta:
        abstract = True

class RealModel(AbstractModel):
    pass

r = RealModel.objects.create(name="Duke")
print(r.name) # Duke
r.hey() # hey 

test = AbstractModel(name="test")
# TypeError: Abstract models cannot be instantiated.

不但 AbstractModel 不會再資料庫長出一張表來。假設我們強行以他實體化,會發生錯誤的。

Multi-table inheritance

這個更簡單了,直接拔掉 abstract = True 即可。和一般 python 繼承無異。

from django.db import models
class AbstractModel(models.Model):
    name = models.CharField(max_length=50)
    
    def hey(self):
        print("hey")

class RealModel(AbstractModel):
    pass

r = RealModel.objects.create(name="Duke")
print(r.name) # Duke
r.hey() # hey 

a = AbstractModel.objects.create(name="test")
print(a.name) # test
a.hey() # hey

直得注意的有兩點:

  1. 親層也會長出一張表,子層也有一張表。子層的表會擁有親層的表的所有屬性,以及他自己定義的其他屬性。但這些屬性是獨立存在在子層的表上的,並不是說子層表會直接連到親層表的屬性。
  2. 子層表會自動長出與親層表的一對一連結,以一個 OneToOneField 代表。假設想要更改這個 field 名字的話,可以在子層中自建 OneToOneField 並更改命名。

Proxy Models

最後,假如你不想創建一個新的表單,只要增加或修改既有模型的方法或是 object manager。也就說,你只是想改變該模型 Python 上的行為,而不想影響到相連的資料庫表單。這時候只要新增一個子層繼承既有的親層,並在子層加上 proxy=True 即可。

from django.db import models
class BaseModel(models.Model):
    name = models.CharField(max_length=50)
    
    def hey(self):
        print("hey")

class ProxyModel(BaseModel):
    def shout(self);
        print(self.name)
    
    def hey(self):
        print("heyhey")

b = BaseModel.objects.create(name="Duke")
print(b.name) # Duke
b.shout() # Duke
b.hey() # heyhey

p = ProxyModel.objects.create(name="test")
# TypeError: Proxy models cannot be instantiated.

Proxy Models 本身不能創建物件,但可以覆蓋或增加親層的既有方法。
這種作法對已上線的專案非常實用,因為既不會影響到資料庫的運行,也可以增加開發者使用的自由度。


上一篇
JWT vs Session
下一篇
Django Model SoftDelete
系列文
學習網頁開發13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言